<cfcomponent>
  <Cfset _i = structNew() />
  <cfset _i.criteriaArray = ArrayNew(1) />

  <cffunction name="init" output="false">
    <cfreturn this />
  </cffunction>

  <cffunction name="addSortCriteria" output="false">
    <cfargument name="criteria" required="true" />
    <cfset ArrayAppend(_i.criteriaArray,arguments.criteria) />
  </cffunction>

<cfscript>
/**
 * Implementation of Hoare's Quicksort algorithm for sorting arrays of arbitrary items.
 * Slight mods by RCamden (added var for comparison)
 *
 * @param arrayToCompare 	 The array to be sorted.
 * @param sorter 	 The comparison UDF.
 * @return Returns a sorted array.
 * @author James Sleeman (james@innovativemedia.co.nz)
 * @version 1, March 12, 2002
 */
function quickSort(arrayToCompare, order, sorter, expression){
	var lesserArray  = ArrayNew(1);
	var greaterArray = ArrayNew(1);
	var pivotArray   = ArrayNew(1);
	var examine      = 2;
	var comparison = 0;
	var f1= 0;
	var f2= 0;
	var newExpression = ".#expression#";

  if(newExpression EQ "."){newExpression="";}

	pivotArray[1] = arrayToCompare[1];

	if (arrayLen(arrayToCompare) LT 2) {
		return arrayToCompare;
	}

	while(examine LTE arrayLen(arrayToCompare)){
    if(order EQ "asc"){
    	try{
        f1 = 	evaluate("arrayToCompare[examine]#newexpression#");
    	}
      catch(any e){
        f1 = "";
      }
    	try{
        f2 = 	evaluate("pivotArray[1]#newexpression#");
    	}
      catch(any e){
        f2 = "";
      }
    }
    else{
    	try{
        f1 = 	evaluate("pivotArray[1]#newexpression#");
    	}
      catch(any e){
        f1 = "";
      }
    	try{
        f2 = 	evaluate("arrayToCompare[examine]#newexpression#");
    	}
      catch(any e){
        f2 = "";
      }
    }

    comparison = sorter(f1, f2);
    if(comparison EQ 2){
    	if(isSimpleValue(f1)){
        throwcf("#f1#","");
    	}
    	if(isSimpleValue(f2)){
        throwcf("#f2#","");
    	}
    }

		switch(comparison) {
			case "-1": {
				arrayAppend(lesserArray, arrayToCompare[examine]);
				break;
			}
			case "0": {
				arrayAppend(pivotArray, arrayToCompare[examine]);
				break;
			}
			case "1": {
				arrayAppend(greaterArray, arrayToCompare[examine]);
				break;
			}
			default: {
				throwcf('Sort function must return -1, 0, 1.  Returned:#comparison#','');
				break;
			}
		}
		examine = examine + 1;
	}

	if (arrayLen(lesserArray)) {
		lesserArray  = quickSort(lesserArray,order, sorter, expression);
	} else {
		lesserArray = arrayNew(1);
	}

	if (arrayLen(greaterArray)) {
		greaterArray = quickSort(greaterArray,order, sorter, expression);
	} else {
		greaterArray = arrayNew(1);
	}

	examine = 1;
	while(examine LTE arrayLen(pivotArray)){
		arrayAppend(lesserArray, pivotArray[examine]);
		examine = examine + 1;
	};

	examine = 1;
	while(examine LTE arrayLen(greaterArray)){
		arrayAppend(lesserArray, greaterArray[examine]);
		examine = examine + 1;
	};

	return lesserArray;
}
</cfscript>

  <cffunction name="sort" output="false" returnType="array">
    <cfargument name="array" type="array" required="true">
    <cfset var curCr = "" />
    <cfset var sortedArray = ArrayNew(1) />
    <cfset var i = "" />
    <cfloop from="1" to="#arrayLen(_i.criteriaArray)#" index="i">
      <cfset curCr = _i.criteriaArray[i] />
      <cfset sortedArray = sortArray(array,curCr.getexpression(),curCr.getdirection(),curCr.gettype())>
    </cfloop>

    <cfreturn sortedArray />
  </cffunction>

  <cffunction name="sortArray" output="false" returnType="array" access="private">
    <cfargument name="array" type="array" required="true">
    <cfargument name="expression" type="string" required="true">
    <cfargument name="direction" type="string" required="true">
    <cfargument name="type" type="any" required="true">
    <cfset var compareFunction =  "" />
    <cfset var result =  "" />
    <cfif arrayLen(arguments.array) EQ 0 >
      <cfreturn arguments.array />
    </cfif>
    <cfif isCustomFunction(type) >
      <cfset compareFunction = arguments.type />
    <cfelse>
      <cfswitch expression="#arguments.type#">
        <cfcase value="date">
          <cfset compareFunction = compareDates />
        </cfcase>
        <cfcase value="textnocase,string">
          <cfset compareFunction = compareTextNoCase/>
        </cfcase>
        <cfcase value="numeric">
          <cfset compareFunction = compareNumbers/>
        </cfcase>

        <cfdefaultcase>
          <cfthrow message="invalid data sort type ""#arguments.type#""">
        </cfdefaultcase>
      </cfswitch>
    </cfif>
    <cfset result = quicksort(arguments.array,arguments.direction,compareFunction,arguments.expression) />
    <cfreturn result />
  </cffunction>

  <cffunction name="compareDates" output="false">
    <cfargument name="node1" required="true" />
    <cfargument name="node2" required="true" />
    <cfset var f1 = arguments.node1 />
    <cfset var f2 = arguments.node2 />
    <cfset var result = 0 />
    <cfif isSimpleValue(f1) and isSimpleValue(f2)
          AND isDate(f1) AND isDate(f2) >
      <cfset result = DateCompare(f1,f2) />
    <cfelseif isSimpleValue(f1) AND isDate(f1)>
      <cfset result = -1 /><!--- Can only compare dates --->
    <cfelseif isSimpleValue(f2) AND isDate(f2)>
      <cfset result = 1 /><!--- Can only compare dates --->
    </cfif>
    <cfreturn result />
  </cffunction>

  <cffunction name="compareNumbers" output="false">
    <cfargument name="node1" required="true" />
    <cfargument name="node2" required="true" />
    <cfset var f1 = arguments.node1 />
    <cfset var f2 = arguments.node2 />
    <cfset var result = 0 />
    <cfif isNumeric(f1) AND isnumeric(f2)>
      <cfif (f1 lt f2) >
        <cfset result= -1 />
      </cfif>
      <cfif (f1 eq f2)>
        <cfset result = 0 />
      </cfif>
      <cfif (f1 gt f2)>
        <cfset result = 1 />
      </cfif>
    <cfelseif isSimpleValue(f1) and isSimpleValue(f2)>
      <!--- put empty strings at the beginning, everything else at the end --->
      <cfif f1 EQ "" >
        <cfset result = -1 />
      <cfelseif f2 EQ "">
        <cfset result = -1 />
      <cfelse>
        <cfset result = 1 />
      </cfif>
    <cfelseif isSimpleValue(f1)>
      <cfset result = -1 /><!--- can only compare simple values --->
    <cfelse>
      <cfset result = 1 /><!--- can only compare simple values --->
    </cfif>
    <cfreturn result />
  </cffunction>

  <cffunction name="compareTextNoCase" output="false">
    <cfargument name="node1" required="true" />
    <cfargument name="node2" required="true" />
    <cfset var f1 = arguments.node1 />
    <cfset var f2 = arguments.node2 />
    <cfset var result = 0 />
    <cfif isSimpleValue(f1) and isSimpleValue(f2)>
      <cfset result = compareNoCase(f1,f2) />
    <cfelseif isSimpleValue(f1)>
      <cfset result = -1 /><!--- can only compare simple values --->
    <cfelse>
      <cfset result = 1 /><!--- can only compare simple values --->
    </cfif>
    <cfreturn result />
  </cffunction>

  <cffunction name="throwcf" output="false">
    <cfargument name="message" required="true" />
    <cfargument name="detail" required="true" />
    <cfthrow message="message: #arguments.message#" detail="detail:#arguments.detail#">
  </cffunction>
</cfcomponent>